#pragma once

#define DEVICE_SCSI_BUFSIZE (65536 - 1024)

#define INQ_DASD 0x00           /* Direct-access device (disk) */
#define INQ_SEQD 0x01           /* Sequential-access device (tape) */
#define INQ_PRTD 0x02           /* Printer device */
#define INQ_PROCD 0x03          /* Processor device */
#define INQ_OPTD 0x04           /* Write once device (optical disk) */
#define INQ_WORM 0x04           /* Write once device (optical disk) */
#define INQ_ROMD 0x05           /* CD-ROM device */
#define INQ_SCAN 0x06           /* Scanner device */
#define INQ_OMEM 0x07           /* Optical Memory device */
#define INQ_JUKE 0x08           /* Medium Changer device (jukebox) */
#define INQ_COMM 0x09           /* Communications device */
#define INQ_IT8_1 0x0A          /* IT8 */
#define INQ_IT8_2 0x0B          /* IT8 */
#define INQ_STARR 0x0C          /* Storage array device */
#define INQ_ENCL 0x0D           /* Enclosure services device */
#define INQ_NODEV 0x1F          /* Unknown or no device */
#define INQ_NOTPR 0x1F          /* Logical unit not present (SCSI-1) */

#define MAX_TOC_ENTRIES 103
struct cd_toc
{
    byte adr, control;
    byte tno;
    byte point;
    byte track;
    int address; // LSN
    int paddress; // LSN
    byte zero;
    byte crc[2];
};
struct cd_toc_head
{
    int first_track, first_track_offset;
    int last_track, last_track_offset;
    int lastaddress; // LSN
    int tracks;
    int points;
    struct cd_toc toc[MAX_TOC_ENTRIES];
};

#define SUB_ENTRY_SIZE 12
#define SUB_CHANNEL_SIZE 96
#define SUBQ_SIZE (4 + 12)

#define AUDIO_STATUS_NOT_SUPPORTED 0x00
#define AUDIO_STATUS_IN_PROGRESS 0x11
#define AUDIO_STATUS_PAUSED 0x12
#define AUDIO_STATUS_PLAY_COMPLETE 0x13
#define AUDIO_STATUS_PLAY_ERROR 0x14
#define AUDIO_STATUS_NO_STATUS 0x15

struct device_info
{
    bool open;
    int type;
    int media_inserted;
    int removable;
    int write_protected;
    int cylinders;
    int trackspercylinder;
    int sectorspertrack;
    int bytespersector;
    int bus, target, lun;
    int unitnum;
    TCHAR label[MAX_DPATH];
    TCHAR mediapath[MAX_DPATH];
    TCHAR vendorid[10];
    TCHAR productid[18];
    TCHAR revision[6];
    TCHAR* backend;
    struct cd_toc_head toc;
};

struct amigascsi
{
    byte* data;
    int len;
    byte cmd[16];
    int cmd_len;
    byte flags;
    byte sensedata[256];
    ushort sense_len;
    ushort cmdactual;
    byte status;
    ushort actual;
    ushort sactual;
};

typedef int (*check_bus_func)(int flags);
typedef int (*open_bus_func)(int flags);
typedef void (*close_bus_func)();
typedef int (*open_device_func)(int, const TCHAR*, int);
typedef void (*close_device_func)(int);
typedef device_info* (*info_device_func)(int, struct device_info*, int);
typedef byte* (*execscsicmd_out_func)(int, byte*, int);
typedef byte* (*execscsicmd_in_func)(int, byte*, int, int*);
typedef int (*execscsicmd_direct_func)(int, struct amigascsi*);

typedef void (*play_subchannel_callback)(byte*, int);
typedef int (*play_status_callback)(int);

typedef int (*pause_func)(int, int);
typedef int (*stop_func)(int);
typedef int (*play_func)(int, int, int, int, play_status_callback, play_subchannel_callback);
typedef uint (*volume_func)(int, ushort, ushort);
typedef int (*qcode_func)(int, byte*, int);
typedef int (*toc_func)(int, struct cd_toc_head*);
typedef int (*read_func)(int, byte*, int, int);
typedef int (*rawread_func)(int, byte*, int, int, int, uint);
typedef int (*write_func)(int, byte*, int, int);
typedef int (*isatapi_func)(int);
typedef int (*ismedia_func)(int, int);
typedef int (*scsiemu_func)(int, byte*);

struct device_functions
{
    const TCHAR* name;
    open_bus_func openbus;
    close_bus_func closebus;
    open_device_func opendev;
    close_device_func closedev;
    info_device_func info;
    execscsicmd_out_func exec_out;
    execscsicmd_in_func exec_in;
    execscsicmd_direct_func exec_direct;

    pause_func pause;
    stop_func stop;
    play_func play;
    volume_func volume;
    qcode_func qcode;
    toc_func toc;
    read_func read;
    rawread_func rawread;
    write_func write;

    isatapi_func isatapi;
    ismedia_func ismedia;

    scsiemu_func scsiemu;
};

// extern struct device_functions* device_func[MAX_TOTAL_SCSI_DEVICES];

extern int device_func_init(CDSlotType flags);
extern void device_func_reset();
extern int sys_command_open(int unitnum);
extern void sys_command_close(int unitnum);
extern int sys_command_isopen(int unitnum);
extern struct device_info* sys_command_info(int unitnum, struct device_info* di, int);
extern int sys_command_cd_pause(int unitnum, int paused);
extern void sys_command_cd_stop(int unitnum);
extern int sys_command_cd_play(int unitnum, int startlsn, int endlsn, int);
extern int sys_command_cd_play(int unitnum, int startlsn, int endlsn, int scan, play_status_callback statusfunc, play_subchannel_callback subfunc);
extern uint sys_command_cd_volume(int unitnum, ushort volume_left, ushort volume_right);
extern int sys_command_cd_qcode(int unitnum, byte*);
extern int sys_command_cd_toc(int unitnum, struct cd_toc_head*);
extern int sys_command_cd_read(int unitnum, byte* data, int block, int size);
extern int sys_command_cd_rawread(int unitnum, byte* data, int sector, int size, int sectorsize);
extern int sys_command_cd_rawread(int unitnum, byte* data, int sector, int size, int sectorsize, byte scsicmd9, byte subs);
extern int sys_command_read(int unitnum, byte* data, int block, int size);
extern int sys_command_write(int unitnum, byte* data, int block, int size);
extern int sys_command_scsi_direct_native(int unitnum, struct amigascsi* as);
extern int sys_command_scsi_direct(int unitnum, uae_ptr request);
extern int sys_command_ismedia(int unitnum, int quick);

extern void scsi_atapi_fixup_pre(byte* scsi_cmd, int* len, byte** data, int* datalen, int* parm);
extern void scsi_atapi_fixup_post(byte* scsi_cmd, int len, byte* olddata, byte* data, int* datalen, int parm);

extern void scsi_log_before(byte* cdb, int cdblen, byte* data, int datalen);
extern void scsi_log_after(byte* data, int datalen, byte* a_sense, int senselen);

extern void blkdev_vsync();

extern int msf2lsn(int msf);
extern int lsn2msf(int lsn);
extern byte frombcd(byte v);
extern byte tobcd(byte v);
extern int fromlongbcd(byte* p);
extern void tolongbcd(byte* p, int v);

extern void blkdev_default_prefs(CConfiguration* conf);
extern void blkdev_fix_prefs(CConfiguration* conf);
extern int isaudiotrack(struct cd_toc_head*, int block);
extern int isdatatrack(struct cd_toc_head*, int block);

enum cd_standard_unit { CD_STANDARD_UNIT_DEFAULT, CD_STANDARD_UNIT_AUDIO, CD_STANDARD_UNIT_CDTV, CD_STANDARD_UNIT_CD32 };

extern int get_standard_cd_unit(enum cd_standard_unit csu);
extern void close_standard_cd_unit(int);
extern void blkdev_cd_change(int unitnum, const TCHAR* name);

extern void blkdev_entergui();
extern void blkdev_exitgui();